package com.dayatang.hrm.compensation.domain;

import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.dayatang.commons.domain.AbstractEntity;
import com.dayatang.commons.domain.InstanceFactory;
import com.dayatang.hrm.compensation.context.CompensationMessageSource;
import com.dayatang.hrm.compensation.domain.exception.CompensationRuntimeException;
import com.dayatang.hrm.staffing.domain.Employee;
import com.dayatang.utils.DateUtils;

/**
 * 员工帐套分配：基于责任模式
 * 
 * @author chencao
 * 
 */
@Entity
@Table(name = "pay_accountability_employee_accountsuite")
@NamedQueries( {
		@NamedQuery(name = "SalaryAccountSuiteEmployeeAssign.findEmployeesBySalaryAccountSuite", query = "select emp from SalaryAccountSuiteEmployeeAssign assign inner join assign.employee emp inner join assign.salaryAccountSuite suite where (suite = :pSuite and suite.abolishDate >= :pQueryDate and assign.fromDate <= :pQueryDate and assign.toDate >= :pQueryDate)"),
		@NamedQuery(name = "SalaryAccountSuiteEmployeeAssign.getValid", query = "select assign from SalaryAccountSuiteEmployeeAssign assign inner join assign.employee emp inner join assign.salaryAccountSuite suite where (suite = :pSuite and suite.abolishDate >= :pQueryDate and emp = :pEmp and assign.fromDate <= :pQueryDate and assign.toDate >= :pQueryDate)"),
		@NamedQuery(name = "SalaryAccountSuiteEmployeeAssign.getValidSalaryAccountSuite", query = "select suite from SalaryAccountSuiteEmployeeAssign assign inner join assign.employee emp inner join assign.salaryAccountSuite suite where (suite.abolishDate >= :pQueryDate and emp = :pEmp and assign.fromDate <= :pQueryDate and assign.toDate >= :pQueryDate)"),
		@NamedQuery(name = "SalaryAccountSuiteEmployeeAssign.isAscriptionAlreadyExistExceptSelf.selfIsNull", query = "select assign from SalaryAccountSuiteEmployeeAssign assign inner join assign.salaryAccountSuite suite inner join assign.employee emp where (emp = :pEmp and suite.abolishDate >= :pFromDate and  ((assign.fromDate <= :pToDate and assign.toDate >= :pToDate ) or (assign.toDate >= :pFromDate and assign.toDate <= :pToDate)))"),
		@NamedQuery(name = "SalaryAccountSuiteEmployeeAssign.isAscriptionAlreadyExistExceptSelf.selfIsExist", query = "select assign from SalaryAccountSuiteEmployeeAssign assign inner join assign.salaryAccountSuite suite inner join assign.employee emp where (emp = :pEmp and suite.abolishDate >= :pFromDate and assign <> :pSelf and  ((assign.fromDate <= :pToDate and assign.toDate >= :pToDate ) or (assign.toDate >= :pFromDate and assign.toDate <= :pToDate)))"),
		@NamedQuery(name = "SalaryAccountSuiteEmployeeAssign.findByDatePeriod", query = "select assign from SalaryAccountSuiteEmployeeAssign assign inner join assign.salaryAccountSuite suite where (suite.abolishDate >= :pFromDate and assign.fromDate <= :pFromDate and assign.toDate >= :pToDate )") })
public class SalaryAccountSuiteEmployeeAssign extends AbstractEntity {

	/**
	 * 
	 */
	private static final long serialVersionUID = -4585374143800627816L;

	private static SalaryAccountSuiteEmployeeAssignRepository salaryAccountSuiteEmployeeAssignRepository;

	protected static MessageSourceAccessor messages = CompensationMessageSource
			.getAccessor();

	// 员工（约束：一个员工只能分配一个帐套）
	@OneToOne
	@JoinColumn(name = "employee_id", nullable = false)
	private Employee employee;
	// 薪资帐套(定义级)
	@ManyToOne
	@JoinColumn(name = "salary_accountsuite_id", nullable = false)
	private SalaryAccountSuite salaryAccountSuite;

	@Column(name = "from_date", nullable = false)
	@Temporal(TemporalType.TIMESTAMP)
	private Date fromDate;

	@Column(name = "to_date")
	@Temporal(TemporalType.TIMESTAMP)
	private Date toDate = DateUtils.MAX_DATE;

	@Column(name = "created_time")
	@Temporal(TemporalType.TIMESTAMP)
	private Date created;

	public SalaryAccountSuiteEmployeeAssign() {
	}

	public SalaryAccountSuiteEmployeeAssign(
			SalaryAccountSuite salaryAccountSuite, Employee employee) {
		this.employee = employee;
		this.salaryAccountSuite = salaryAccountSuite;
	}

	public static SalaryAccountSuiteEmployeeAssignRepository getSalaryAccountSuiteEmployeeAssignRepository() {
		if (salaryAccountSuiteEmployeeAssignRepository == null) {
			salaryAccountSuiteEmployeeAssignRepository = InstanceFactory
					.getInstance(SalaryAccountSuiteEmployeeAssignRepository.class);
		}
		return salaryAccountSuiteEmployeeAssignRepository;
	}

	public static void setSalaryAccountSuiteEmployeeAssignRepository(
			SalaryAccountSuiteEmployeeAssignRepository salaryAccountSuiteemployeeAssignRepository) {
		SalaryAccountSuiteEmployeeAssign.salaryAccountSuiteEmployeeAssignRepository = salaryAccountSuiteemployeeAssignRepository;
	}

	public static MessageSourceAccessor getMessages() {
		return messages;
	}

	public static void setMessages(MessageSourceAccessor messages) {
		SalaryAccountSuiteEmployeeAssign.messages = messages;
	}

	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public SalaryAccountSuite getSalaryAccountSuite() {
		return salaryAccountSuite;
	}

	public void setSalaryAccountSuite(SalaryAccountSuite salaryAccountSuite) {
		this.salaryAccountSuite = salaryAccountSuite;
	}

	public Date getFromDate() {
		return fromDate;
	}

	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}

	public Date getToDate() {
		return toDate;
	}

	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof SalaryAccountSuiteEmployeeAssign == false) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		SalaryAccountSuiteEmployeeAssign rhs = (SalaryAccountSuiteEmployeeAssign) obj;
		return new EqualsBuilder().append(employee.getId(),
				rhs.employee.getId()).append(salaryAccountSuite.getId(),
				rhs.salaryAccountSuite.getId()).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder(17, 37).append(employee.getId()).append(
				salaryAccountSuite.getId()).toHashCode();
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}

	/*
	 * =======================================
	 * 
	 * action
	 * 
	 * ========================================
	 */

	void save() {
		if (isNew()) {
			setCreated(new Date());
		}

		if (fromDate == null) {
			setFromDate(created);
		}

		validateAscription();

		getSalaryAccountSuiteEmployeeAssignRepository().save(this);
	}

	public void remove() {
		getSalaryAccountSuiteEmployeeAssignRepository().remove(this);
	}

	public void terminate(Date terminateDate) {
		setToDate(terminateDate);
		save();
	}
	
	public void validateAscription() {

		Assert
				.notNull(
						employee,
						messages
								.getMessage(
										"SalaryAccountSuiteEmployeeAssign.validateAscription.employeeIsNull",
										"为薪资帐套分配的员工不允许为空。"));

		Assert
				.notNull(
						salaryAccountSuite,
						messages
								.getMessage(
										"SalaryAccountSuiteEmployeeAssign.validateAscription.salaryAccountSuiteIsNull",
										"员工所分配的薪资帐套不允许为空。"));

		if (isAscriptionAlreadyExist()) {
			throw new CompensationRuntimeException(
					messages
							.getMessage(
									"SalaryAccountSuiteEmployeeAssign.validateAscription.employeeAlreadyAssigned",
									"该员工已经被指派给了其他薪资帐套。"));
		}

	}

	public boolean isAscriptionAlreadyExist() {
		
		if (isNew()) {
			return getSalaryAccountSuiteEmployeeAssignRepository()
					.isAscriptionAlreadyExistExceptSelf(salaryAccountSuite,
							employee, fromDate, toDate, null);
		} else {
			return getSalaryAccountSuiteEmployeeAssignRepository()
					.isAscriptionAlreadyExistExceptSelf(salaryAccountSuite,
							employee, fromDate, toDate, this);
		}
	}


//	public static void terminate(SalaryAccountSuite salaryAccountSuite) {
//		getSalaryAccountSuiteEmployeeAssignRepository().terminate(
//				salaryAccountSuite);
//	}

	/**
	 * 根据指定日期，查找当时薪资帐套下分配的有效员工列表
	 * 
	 * @param salaryAccountSuite
	 * @param queryDate
	 * @return
	 */
	static List<Employee> findEmployeesBySalaryAccountSuite(
			SalaryAccountSuite salaryAccountSuite, Date queryDate) {
		return getSalaryAccountSuiteEmployeeAssignRepository()
				.findEmployeesBySalaryAccountSuite(salaryAccountSuite,
						queryDate);
	}

	/**
	 * 查找时间段内，有效的“薪资帐套-员工”归属关系
	 * 
	 * @param fromDate
	 * @param toDate
	 * @return
	 */
	public static List<SalaryAccountSuiteEmployeeAssign> findByDatePeriod(
			Date fromDate, Date toDate) {
		return getSalaryAccountSuiteEmployeeAssignRepository()
				.findByDatePeriod(fromDate, toDate);
	}

	/**
	 * 在指定日期，是否存在有效的薪资帐套同员工的归属关系
	 * 
	 * @param salaryAccountSuite
	 * @param employee
	 * @param queryDate
	 * @return
	 */
	public static boolean isAscriptionValid(
			SalaryAccountSuite salaryAccountSuite, Employee employee,
			Date queryDate) {

		SalaryAccountSuiteEmployeeAssign assign = getValid(salaryAccountSuite,
				employee, queryDate);

		if (assign == null) {
			return false;
		} else {
			return true;
		}

	}
	
	public static SalaryAccountSuiteEmployeeAssign get(Long id) {
		return getSalaryAccountSuiteEmployeeAssignRepository().get(id);
	}
	
	/**
	 * 在指定日期下，根据薪资帐套和员工查找其有效关系，找不到返回null。
	 * 
	 * @param salaryAccountSuite
	 * @param employee
	 * @param queryDate
	 * @return
	 */
	public static SalaryAccountSuiteEmployeeAssign getValid(
			SalaryAccountSuite salaryAccountSuite, Employee employee,
			Date queryDate) {
		return getSalaryAccountSuiteEmployeeAssignRepository().getValid(
				salaryAccountSuite, employee, queryDate);
	}

	/**
	 * 根据指定日期，找到员工所属的有效薪资帐套，找不到返回null。
	 * 
	 * @param employee
	 * @param queryDate
	 * @return
	 */
	public static SalaryAccountSuite getValidSalaryAccountSuite(
			Employee employee, Date queryDate) {
		return getSalaryAccountSuiteEmployeeAssignRepository()
				.getValidSalaryAccountSuite(employee, queryDate);
	}

	/**
	 * 为薪资帐套指派员工
	 * 
	 * @param employee
	 * @param accountSuite
	 */
	public static void assignEmployeeToSalaryAccountSuite(Employee employee,
			SalaryAccountSuite accountSuite, Date fromDate, Date toDate) {

		// 约束：员工只能属于一个帐套
		SalaryAccountSuiteEmployeeAssign assign = new SalaryAccountSuiteEmployeeAssign();

		assign.setSalaryAccountSuite(accountSuite);
		assign.setEmployee(employee);
		assign.setFromDate(fromDate);
		if (toDate != null) {
			assign.setToDate(toDate);
		}
		
		assign.save();

	}

}
